//-----------------------------------------------------------------------------
// Global constants
//-----------------------------------------------------------------------------
static const int    MAX_SAMPLES            = 16;    // Maximum texture grabs
static const float  BRIGHT_PASS_THRESHOLD  = 5.0f;  // Threshold for BrightPass filter
static const float  BRIGHT_PASS_OFFSET     = 8.0f; // Offset for BrightPass filter

// The per-color weighting to be used for luminance calculations in RGB order.
static const float3 LUMINANCE_VECTOR  = float3(0.2125f, 0.7154f, 0.0721f);

//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------

// Transformation matrices
float4x4 WorldViewProj: WorldViewProjection;   // Object space to view space

float QuadScreenSize = 1024;
    
// Contains sampling offsets used by the techniques
float2 SampleOffsets[MAX_SAMPLES]: Float4ArrayParameter0;
float4 SampleWeights[MAX_SAMPLES]: Float4ArrayParameter1;

// Tone mapping variables
float  MiddleGray : FloatParameter0;       // The middle gray key value
float  BloomScale : FloatParameter1;       // Bloom process multiplier
float  ElapsedTime : ElapsedTime;      // Time in seconds since the last calculation

//-----------------------------------------------------------------------------
// Texture samplers
//-----------------------------------------------------------------------------
texture Tex0 : Texture0;
texture Tex1 : Texture1;
texture Tex2 : Texture2;
sampler2D SamplerTex0 = sampler_state{  Texture = (Tex0);    MIPFILTER = NONE;    MAGFILTER = POINT;    MINFILTER = POINT;  ADDRESSU = CLAMP; ADDRESSV=CLAMP;};
sampler2D SamplerTex0Clamp = sampler_state{  Texture = (Tex0);    MIPFILTER = NONE;    MAGFILTER = POINT;    MINFILTER = POINT;  ADDRESSU = MIRROR; ADDRESSV=MIRROR;  };
sampler2D SamplerTex0Linear = sampler_state{  Texture = (Tex0);    MIPFILTER = NONE;    MAGFILTER = LINEAR;    MINFILTER = LINEAR; };
sampler2D SamplerTex1 = sampler_state{  Texture = (Tex1);    MIPFILTER = NONE;    MAGFILTER = POINT;    MINFILTER = POINT;  };
sampler2D SamplerTex1Linear = sampler_state{  Texture = (Tex1);    MIPFILTER = NONE;    MAGFILTER = LINEAR;    MINFILTER = LINEAR;ADDRESSU = CLAMP; ADDRESSV=CLAMP;  };
sampler2D SamplerTex2 = sampler_state{  Texture = (Tex2);    MIPFILTER = NONE;    MAGFILTER = POINT;    MINFILTER = POINT; };

//-----------------------------------------------------------------------------
// Vertex shaders
//-----------------------------------------------------------------------------




struct VS_INPUT{
	float4 pos:POSITION;
	float2 UV:TEXCOORD0;
};

struct VS_OUTPUT{
    float4 pos:POSITION;
	float2 UV:TEXCOORD0;
};

VS_OUTPUT mainVS(VS_INPUT IN){
    VS_OUTPUT output;
    output.pos=mul(IN.pos, WorldViewProj);
	
	float2 texelSize = 1.0 / QuadScreenSize;
	output.UV=IN.UV+texelSize*0.5f;
    return output;
}

//-----------------------------------------------------------------------------
// Pixel shaders
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Name: SampleLumInitial
// Type: Pixel shader                                      
// Desc: Sample the luminance of the source image using a kernal of sample
//       points, and return a scaled image containing the log() of averages
//-----------------------------------------------------------------------------
float4 SampleLumInitial
    (
    in float2 vScreenPosition : TEXCOORD0
    ) : COLOR
{
    float3 vSample = 0.0f;
    float  fLogLumSum = 0.0f;

    for(int iSample = 0; iSample < 9; iSample++)
    {
        // Compute the sum of log(luminance) throughout the sample points
        vSample = tex2D(SamplerTex0Linear, vScreenPosition+SampleOffsets[iSample]);
        fLogLumSum += log(dot(vSample, LUMINANCE_VECTOR)+0.0001f);		
		
    }
    
    // Divide the sum to complete the average
    fLogLumSum /= 9;
    return float4(fLogLumSum, fLogLumSum, fLogLumSum, 1.0f);
}




//-----------------------------------------------------------------------------
// Name: SampleLumIterative
// Type: Pixel shader                                      
// Desc: Scale down the luminance texture by blending sample points
//-----------------------------------------------------------------------------
float4 SampleLumIterative
    (
    in float2 vScreenPosition : TEXCOORD0
    ) : COLOR
{
    float fResampleSum = 0.0f; 
    
    for(int iSample = 0; iSample < 16; iSample++)
    {
        // Compute the sum of luminance throughout the sample points
        fResampleSum += tex2D(SamplerTex0, vScreenPosition+SampleOffsets[iSample]).x;
    }
    
    // Divide the sum to complete the average
    fResampleSum /= 16;

    return float4(fResampleSum, fResampleSum, fResampleSum, 1.0f);
}




//-----------------------------------------------------------------------------
// Name: SampleLumFinal
// Type: Pixel shader                                      
// Desc: Extract the average luminance of the image by completing the averaging
//       and taking the exp() of the result
//-----------------------------------------------------------------------------
float4 SampleLumFinal
    (
    in float2 vScreenPosition : TEXCOORD0
    ) : COLOR
{
    float fResampleSum = 0.0f;
    
    for(int iSample = 0; iSample < 16; iSample++)
    {
        // Compute the sum of luminance throughout the sample points
        fResampleSum += tex2D(SamplerTex0, vScreenPosition+SampleOffsets[iSample]).x;
    }
    
    // Divide the sum to complete the average, and perform an exp() to complete
    // the average luminance calculation
    fResampleSum = max(0,exp(fResampleSum/16));
	return float4(fResampleSum, fResampleSum, fResampleSum, 1.0f);
}

//-----------------------------------------------------------------------------
// Name: CalculateAdaptedLumPS
// Type: Pixel shader                                      
// Desc: Calculate the luminance that the camera is current adapted to, using
//       the most recented adaptation level, the current scene luminance, and
//       the time elapsed since last calculated
//-----------------------------------------------------------------------------
float4 CalculateAdaptedLumPS
    (
    in float2 vScreenPosition : TEXCOORD0
    ) : COLOR
{
    float fAdaptedLum = max(0,tex2D(SamplerTex0, float2(0.5f, 0.5f)).x);
    float fCurrentLum = tex2D(SamplerTex1, float2(0.5f, 0.5f)).x;
    // The user's adapted luminance level is simulated by closing the gap between
    // adapted luminance and current luminance by 2% every frame, based on a
    // 30 fps rate. This is not an accurate model of human adaptation, which can
    // take longer than half an hour.
	float fDiff = (fCurrentLum - fAdaptedLum);
	float fFactor = 0;
	if (fDiff>0)
		fFactor = ( 1 - pow(0.98f,0.005f*ElapsedTime ));
	else
		fFactor = ( 1 - pow(0.98f,0.05f*ElapsedTime ));
    float fNewAdaptation = fAdaptedLum + fDiff * fFactor;	
	//float fNewAdaptation = fAdaptedLum + (fCurrentLum - fAdaptedLum) * 0.001f;
	return float4(fNewAdaptation, fNewAdaptation, fNewAdaptation, 1.0f);
}




//-----------------------------------------------------------------------------
// Name: FinalScenePassPS
// Type: Pixel shader                                      
// Desc: Perform blue shift, tone map the scene, and add post-processed light
//       effects
//-----------------------------------------------------------------------------
float4 FinalScenePassPS
    (
    in float2 vScreenPosition : TEXCOORD0
    ) : COLOR
{
    float4 vSample = tex2D(SamplerTex0, vScreenPosition);
	float4 vBloom = tex2D(SamplerTex1Linear, vScreenPosition);
    float fAdaptedLum = tex2D(SamplerTex2, float2(0.5f, 0.5f)).x;

    // Map the high range of color values into a range appropriate for
    // display, taking into account the user's adaptation level, and selected
    // values for for middle gray and white cutoff.
	//fAdaptedLum *= MiddleGray/(fAdaptedLum + 0.001f);
	fAdaptedLum = MiddleGray/(fAdaptedLum + 0.001f);
	fAdaptedLum /= fAdaptedLum+1;
	vSample.xyz *= fAdaptedLum;
	
	//vSample.xyz *= 0.18f/(fAdaptedLum + 0.001f);
	//vSample.xyz *= (1.0f + vSample.xyz/1.5f);
	//vSample.rgb /= (1.0f + vSample.xyz);
	
	//vSample.xyz /= fAdaptedLum*2;
	
    
    // Add the star and bloom post processing effects
    vSample += BloomScale * vBloom;
	
    return vSample;
}




//-----------------------------------------------------------------------------
// Name: DownScale4x4PS
// Type: Pixel shader                                      
// Desc: Scale the source texture down to 1/16 scale
//-----------------------------------------------------------------------------
float4 DownScale4x4PS
    (
    in float2 vScreenPosition : TEXCOORD0
    ) : COLOR
{
	
    float4 sample = 0.0f;

	for( int i=0; i < 16; i++ )
	{
		sample += tex2D( SamplerTex0, vScreenPosition + SampleOffsets[i] );
	}
    
	return sample / 16;
}




//-----------------------------------------------------------------------------
// Name: DownScale2x2PS
// Type: Pixel shader                                      
// Desc: Scale the source texture down to 1/4 scale
//-----------------------------------------------------------------------------
float4 DownScale2x2PS
    (
    in float2 vScreenPosition : TEXCOORD0
    ) : COLOR
{
	
    float4 sample = 0.0f;

	for( int i=0; i < 4; i++ )
	{
		sample += tex2D( SamplerTex0, vScreenPosition + SampleOffsets[i] );
	}
    
	return sample / 4;
}




//-----------------------------------------------------------------------------
// Name: GaussBlur5x5PS
// Type: Pixel shader                                      
// Desc: Simulate a 5x5 kernel gaussian blur by sampling the 12 points closest
//       to the center point.
//-----------------------------------------------------------------------------
float4 GaussBlur5x5PS
    (
    in float2 vScreenPosition : TEXCOORD0
    ) : COLOR
{
	
    float4 sample = 0.0f;

	for( int i=0; i < 13; i++ )
	{
		sample += SampleWeights[i] * tex2D( SamplerTex0Clamp, vScreenPosition + SampleOffsets[i] );
	}

	return sample;
}




//-----------------------------------------------------------------------------
// Name: BrightPassFilterPS
// Type: Pixel shader                                      
// Desc: Perform a high-pass filter on the source texture
//-----------------------------------------------------------------------------
float4 BrightPassFilterPS
    (
    in float2 vScreenPosition : TEXCOORD0
    ) : COLOR
{
	float4 vSample = tex2D( SamplerTex0, vScreenPosition );
	float  fAdaptedLum = tex2D( SamplerTex1, float2(0.5f, 0.5f) ).x;
	
	// Determine what the pixel's value will be after tone-mapping occurs
	vSample.rgb *= MiddleGray/(fAdaptedLum + 0.001f);
	
	// Subtract out dark pixels
	vSample.rgb -= BRIGHT_PASS_THRESHOLD;
	
	// Clamp to 0
	vSample = max(vSample, 0.0f);
	
	// Map the resulting value into the 0 to 1 range. Higher values for
	// BRIGHT_PASS_OFFSET will isolate lights from illuminated scene 
	// objects.
	vSample.rgb /= (BRIGHT_PASS_OFFSET+vSample);
    
	return vSample;
}




//-----------------------------------------------------------------------------
// Name: BloomPS
// Type: Pixel shader                                      
// Desc: Blur the source image along one axis using a gaussian
//       distribution. Since gaussian blurs are separable, this shader is called 
//       twice; first along the horizontal axis, then along the vertical axis.
//-----------------------------------------------------------------------------
float4 BloomPS
    (
    in float2 vScreenPosition : TEXCOORD0
    ) : COLOR
{
    
    float4 vSample = 0.0f;
    float4 vColor = 0.0f;
        
    float2 vSamplePosition;
    
    // Perform a one-directional gaussian blur
    for(int iSample = 0; iSample < 15; iSample++)
    {
        vSamplePosition = vScreenPosition + SampleOffsets[iSample];
        vColor = tex2D(SamplerTex0Clamp, vSamplePosition );
        vSample += SampleWeights[iSample]*vColor;
    }
    
    return vSample;
}

//-----------------------------------------------------------------------------
// Techniques
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Name: Bloom
// Type: Technique                                     
// Desc: Performs a single horizontal or vertical pass of the blooming filter
//-----------------------------------------------------------------------------
technique Bloom
{
    pass P0
    {        
        VertexShader = compile vs_2_0 mainVS();
        PixelShader  = compile ps_2_0 BloomPS();
    }

}

//-----------------------------------------------------------------------------
// Name: SampleAvgLum
// Type: Technique                                     
// Desc: Takes the HDR Scene texture as input and starts the process of 
//       determining the average luminance by converting to grayscale, taking
//       the log(), and scaling the image to a single pixel by averaging sample 
//       points.
//-----------------------------------------------------------------------------
technique SampleAvgLum
{
    pass P0
    {
        VertexShader = compile vs_2_0 mainVS();
		PixelShader  = compile ps_2_0 SampleLumInitial();
    }
}




//-----------------------------------------------------------------------------
// Name: ResampleAvgLum
// Type: Technique                                     
// Desc: Continue to scale down the luminance texture
//-----------------------------------------------------------------------------
technique ResampleAvgLum
{
    pass P0
    {
        VertexShader = compile vs_2_0 mainVS();
        PixelShader  = compile ps_2_0 SampleLumIterative();
    }
}




//-----------------------------------------------------------------------------
// Name: ResampleAvgLumExp
// Type: Technique                                     
// Desc: Sample the texture to a single pixel and perform an exp() to complete
//       the evalutation
//-----------------------------------------------------------------------------
technique ResampleAvgLumExp
{
    pass P0
    {
        VertexShader = compile vs_2_0 mainVS();
        PixelShader  = compile ps_2_0 SampleLumFinal();
    }
}




//-----------------------------------------------------------------------------
// Name: CalculateAdaptedLum
// Type: Technique                                     
// Desc: Determines the level of the user's simulated light adaptation level
//       using the last adapted level, the current scene luminance, and the
//       time since last calculation
//-----------------------------------------------------------------------------
technique CalculateAdaptedLum
{
    pass P0
    {
        VertexShader = compile vs_2_0 mainVS();
        PixelShader  = compile ps_2_0 CalculateAdaptedLumPS();
    }
}




//-----------------------------------------------------------------------------
// Name: DownScale4x4
// Type: Technique                                     
// Desc: Scale the source texture down to 1/16 scale
//-----------------------------------------------------------------------------
technique DownScale4x4
{
    pass P0
    {
        VertexShader = compile vs_2_0 mainVS();
        PixelShader  = compile ps_2_0 DownScale4x4PS();
    }
}




//-----------------------------------------------------------------------------
// Name: DownScale2x2
// Type: Technique                                     
// Desc: Scale the source texture down to 1/4 scale
//-----------------------------------------------------------------------------
technique DownScale2x2
{
    pass P0
    {
        VertexShader = compile vs_2_0 mainVS();
        PixelShader  = compile ps_2_0 DownScale2x2PS();
    }
}




//-----------------------------------------------------------------------------
// Name: GaussBlur5x5
// Type: Technique                                     
// Desc: Simulate a 5x5 kernel gaussian blur by sampling the 12 points closest
//       to the center point.
//-----------------------------------------------------------------------------
technique GaussBlur5x5
{
    pass P0
    {
        VertexShader = compile vs_2_0 mainVS();
        PixelShader  = compile ps_2_0 GaussBlur5x5PS();
    }
}

//-----------------------------------------------------------------------------
// Name: BrightPassFilter
// Type: Technique                                     
// Desc: Perform a high-pass filter on the source texture
//-----------------------------------------------------------------------------
technique BrightPassFilter
{
    pass P0
    {
        VertexShader = compile vs_2_0 mainVS();
        PixelShader  = compile ps_2_0 BrightPassFilterPS();
    }
}





//-----------------------------------------------------------------------------
// Name: FinalScenePass
// Type: Technique                                     
// Desc: Minimally transform and texture the incoming geometry
//-----------------------------------------------------------------------------
technique FinalScenePass
{
    pass P0
    {
        VertexShader = compile vs_3_0 mainVS();
        PixelShader  = compile ps_3_0 FinalScenePassPS();
    }
}
